import pandas as pd
import numpy as np
from tqdm import tqdm
import heapq
import matplotlib.pyplot as plt
import seaborn as sns
from collections import defaultdict

# 设置中文显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False


def load_and_preprocess_data(file_path):
    """加载并预处理供应商和订单数据"""
    try:
        print("开始加载数据...")
        df = pd.read_excel(file_path)
        print(f"数据加载成功，共{len(df)}条交易记录")
        print(f"数据包含以下列：{list(df.columns)}")

        # 确保关键列存在
        required_columns = ['supplier_id', 'order_rating']
        for col in required_columns:
            if col not in df.columns:
                raise ValueError(f"数据中缺少必要的列 '{col}'，分析无法进行")

        # 检查评分列是否为数值类型
        if not pd.api.types.is_numeric_dtype(df['order_rating']):
            print("警告：order_rating列不是数值类型，将尝试转换")
            df['order_rating'] = pd.to_numeric(df['order_rating'], errors='coerce')
            df = df.dropna(subset=['order_rating'])

        return df
    except Exception as e:
        print(f"数据加载失败: {e}")
        raise


def calculate_supplier_metrics(df):
    """计算每个供应商的基本绩效指标"""
    print("正在计算供应商基本绩效指标...")

    supplier_metrics = df.groupby('supplier_id').agg(
        total_orders=('order_rating', 'count'),
        avg_rating=('order_rating', 'mean'),
        high_rating_orders=('order_rating', lambda x: (x >= 4).sum()),
        low_rating_orders=('order_rating', lambda x: (x <= 2).sum()),
        rating_std=('order_rating', 'std')
    ).reset_index()

    # 计算高评分订单比例和低评分订单比例
    supplier_metrics['high_rating_ratio'] = supplier_metrics['high_rating_orders'] / supplier_metrics['total_orders']
    supplier_metrics['low_rating_ratio'] = supplier_metrics['low_rating_orders'] / supplier_metrics['total_orders']

    return supplier_metrics


def heuristic_function(supplier_id, supplier_metrics, target_metrics):
    """启发式函数：评估供应商与目标绩效的距离"""
    # 获取供应商的当前指标
    supplier = supplier_metrics[supplier_metrics['supplier_id'] == supplier_id].iloc[0]

    # 计算与目标指标的加权距离
    distance = 0
    for metric, weight in target_metrics.items():
        if metric == 'supplier_id':
            continue

        # 标准化指标值
        if metric in ['avg_rating', 'high_rating_ratio']:
            # 越高越好的指标
            ideal_value = supplier_metrics[metric].max()
            current_value = supplier[metric]
            distance += weight * (1 - (current_value / ideal_value)) if ideal_value > 0 else 0
        elif metric in ['low_rating_ratio', 'rating_std']:
            # 越低越好的指标
            ideal_value = supplier_metrics[metric].min()
            current_value = supplier[metric]
            distance += weight * ((current_value - ideal_value) / (supplier_metrics[metric].max() - ideal_value)) if (
                                                                                                                                 supplier_metrics[
                                                                                                                                     metric].max() - ideal_value) > 0 else 0

    return distance


def a_star_search(supplier_metrics, target_metrics, min_orders=10):
    """使用A*算法搜索最优供应商"""
    # 过滤订单数量不足的供应商
    valid_suppliers = supplier_metrics[supplier_metrics['total_orders'] >= min_orders]['supplier_id'].tolist()

    if not valid_suppliers:
        print(f"没有满足最小订单数({min_orders})的供应商")
        return []

    # 初始化开放列表和关闭列表
    open_list = []
    closed_set = set()

    # 对每个供应商计算初始启发式值并加入开放列表
    for supplier_id in valid_suppliers:
        h = heuristic_function(supplier_id, supplier_metrics, target_metrics)
        heapq.heappush(open_list, (h, supplier_id))

    print("正在使用A*算法评估供应商绩效...")
    progress_bar = tqdm(total=len(valid_suppliers), desc="评估供应商")

    evaluated_suppliers = []

    while open_list:
        # 从开放列表中取出f值最小的节点
        h, supplier_id = heapq.heappop(open_list)

        # 检查是否已经在关闭列表中
        if supplier_id in closed_set:
            continue

        # 添加到关闭列表
        closed_set.add(supplier_id)

        # 获取供应商的详细指标
        supplier_data = supplier_metrics[supplier_metrics['supplier_id'] == supplier_id].iloc[0].to_dict()
        supplier_data['heuristic_score'] = h

        evaluated_suppliers.append(supplier_data)

        # 更新进度条
        progress_bar.update(1)

    progress_bar.close()

    # 按启发式分数排序（分数越低越接近目标）
    evaluated_suppliers.sort(key=lambda x: x['heuristic_score'])

    return evaluated_suppliers


def visualize_supplier_performance(evaluated_suppliers, top_n=10):
    """可视化供应商绩效评估结果"""
    if not evaluated_suppliers or len(evaluated_suppliers) == 0:
        print("没有足够的供应商数据进行可视化")
        return pd.DataFrame()

    # 转换为DataFrame便于处理
    df = pd.DataFrame(evaluated_suppliers)

    # 创建一个2x2的子图布局
    fig, axes = plt.subplots(2, 2, figsize=(16, 12))

    # 1. 供应商评分分布
    sns.histplot(df['avg_rating'], kde=True, ax=axes[0, 0])
    axes[0, 0].set_title('供应商平均评分分布')
    axes[0, 0].set_xlabel('平均评分')
    axes[0, 0].set_ylabel('供应商数量')

    # 2. Top N供应商评分对比（修复palette警告，使用x作为hue）
    sns.barplot(x='supplier_id', y='avg_rating', data=df.head(top_n),
                ax=axes[0, 1], palette='viridis', hue='avg_rating', legend=False)
    axes[0, 1].set_title(f'Top {top_n} 供应商平均评分')
    axes[0, 1].set_xlabel('供应商ID')
    axes[0, 1].set_ylabel('平均评分')

    # 3. 高评分订单比例 vs 低评分订单比例
    sns.scatterplot(x='high_rating_ratio', y='low_rating_ratio', data=df,
                    hue='avg_rating', size='total_orders', sizes=(50, 300), ax=axes[1, 0])
    axes[1, 0].set_title('高评分订单比例 vs 低评分订单比例')
    axes[1, 0].set_xlabel('高评分订单比例')
    axes[1, 0].set_ylabel('低评分订单比例')

    # 4. 绩效评估雷达图（修复set_thetagrids错误，改用极坐标图）
    radar_metrics = ['avg_rating', 'high_rating_ratio', 'low_rating_ratio', 'rating_std']
    num_metrics = len(radar_metrics)

    # 规范化数据
    radar_data = df.copy()
    for metric in radar_metrics:
        if metric in ['avg_rating', 'high_rating_ratio']:
            # 越高越好的指标
            radar_data[metric] = radar_data[metric] / radar_data[metric].max()
        elif metric in ['low_rating_ratio', 'rating_std']:
            # 越低越好的指标
            radar_data[metric] = 1 - (radar_data[metric] - radar_data[metric].min()) / (
                        radar_data[metric].max() - radar_data[metric].min())

    # 只显示Top N供应商的雷达图
    top_radar_data = radar_data.head(top_n)

    # 创建极坐标图
    ax = axes[1, 1]
    ax.set_title(f'Top {top_n} 供应商绩效雷达图', pad=20)
    ax = plt.subplot(224, polar=True)  # 指定极坐标子图

    # 设置角度和标签
    angles = np.linspace(0, 2 * np.pi, num_metrics, endpoint=False).tolist()
    categories = radar_metrics
    ax.set_xticks(angles)
    ax.set_xticklabels(categories, fontsize=10)

    # 闭合雷达图数据
    for i, row in top_radar_data.iterrows():
        values = row[radar_metrics].tolist()
        values += values[:1]  # 闭合数据
        angles_plot = angles + angles[:1]


        # 绘制线条和填充
        ax.plot(angles_plot, values, linewidth=2, label=f"供应商{int(row['supplier_id'])}")
        ax.fill(angles_plot, values, alpha=0.1)

    ax.set_ylim(0, 1.1)
    ax.legend(loc='upper right', bbox_to_anchor=(1.3, 0))

    plt.tight_layout()
    plt.savefig('supplier_performance.png', dpi=300, bbox_inches='tight')
    plt.show()

    return df


def categorize_suppliers(supplier_df):
    """将供应商分类为不同的合作等级"""
    # 基于平均评分和高评分订单比例进行分类
    high_threshold = supplier_df['avg_rating'].quantile(0.75)
    low_threshold = supplier_df['avg_rating'].quantile(0.25)

    high_ratio_threshold = supplier_df['high_rating_ratio'].quantile(0.75)
    low_ratio_threshold = supplier_df['high_rating_ratio'].quantile(0.25)

    supplier_df['cooperation_level'] = '中等'

    # 高绩效供应商
    supplier_df.loc[(supplier_df['avg_rating'] >= high_threshold) &
                    (supplier_df['high_rating_ratio'] >= high_ratio_threshold),
    'cooperation_level'] = '深化合作'

    # 低绩效供应商
    supplier_df.loc[(supplier_df['avg_rating'] <= low_threshold) |
                    (supplier_df['low_rating_ratio'] >= low_ratio_threshold),
    'cooperation_level'] = '需改进'

    # 打印分类结果
    level_counts = supplier_df['cooperation_level'].value_counts()
    print("\n供应商合作等级分类结果:")
    for level, count in level_counts.items():
        print(f"- {level}: {count}家供应商")

    # 可视化分类结果（修复palette警告，显式设置hue）
    plt.figure(figsize=(10, 6))
    sns.countplot(x='cooperation_level', data=supplier_df,
                  order=['深化合作', '中等', '需改进'], palette='coolwarm', hue='cooperation_level')
    plt.title('供应商合作等级分布')
    plt.xlabel('合作等级')
    plt.ylabel('供应商数量')
    plt.legend_.remove()  # 移除重复图例
    plt.tight_layout()
    plt.savefig('supplier_categorization.png', dpi=300, bbox_inches='tight')

    return supplier_df


def main():
    # 文件路径 - 需要替换为实际文件路径
    file_path = 'A_xxx.xlsx'

    # 加载和预处理数据
    df = load_and_preprocess_data(file_path)

    # 计算供应商基本绩效指标
    supplier_metrics = calculate_supplier_metrics(df)

    # 设置目标绩效指标和权重
    target_metrics = {
        'avg_rating': 0.4,  # 平均评分权重
        'high_rating_ratio': 0.3,  # 高评分订单比例权重
        'low_rating_ratio': 0.2,  # 低评分订单比例权重
        'rating_std': 0.1  # 评分稳定性权重
    }

    # 使用A*算法评估供应商绩效
    evaluated_suppliers = a_star_search(supplier_metrics, target_metrics, min_orders=5)

    if not evaluated_suppliers:
        print("没有找到符合条件的供应商")
        return

    # 可视化供应商绩效
    supplier_df = visualize_supplier_performance(evaluated_suppliers, top_n=5)

    # 对供应商进行分类
    categorized_suppliers = categorize_suppliers(supplier_df)

    # 输出Top 5和Bottom 5供应商
    print("\nTop 5 优质供应商:")
    for i, supplier in enumerate(categorized_suppliers.head(5).to_dict('records'), 1):
        print(f"{i}. 供应商ID: {supplier['supplier_id']}, 平均评分: {supplier['avg_rating']:.2f}, "
              f"高评分比例: {supplier['high_rating_ratio']:.2%}, 合作等级: {supplier['cooperation_level']}")

    print("\nBottom 5 需改进供应商:")
    for i, supplier in enumerate(categorized_suppliers.tail(5).to_dict('records'), 1):
        print(f"{i}. 供应商ID: {supplier['supplier_id']}, 平均评分: {supplier['avg_rating']:.2f}, "
              f"低评分比例: {supplier['low_rating_ratio']:.2%}, 合作等级: {supplier['cooperation_level']}")


if __name__ == "__main__":
    main()